package com.example.springdemo02.config;

import com.example.springdemo02.custom.CustomCommandLineRunner;
import com.example.springdemo02.custom.CustomProperties;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

@EnableConfigurationProperties(CustomProperties.class)
public class AutoConfig {
    @Bean
    @ConditionalOnProperty(prefix = "eazen.auto",name = "enable",havingValue = "true")
    CommandLineRunner createCustomCommandLineRunner(Environment environment){
        return new CustomCommandLineRunner(environment);
    }

    /**
     * 阅读 Spring Ioc容器刷新过程的源码后,发现我们可以自定义一个 {@link ApplicationEventMulticaster}
     * 代替默认的事件发布器,注意:Bean的名称必须是{@link  AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME}。
     * Spring框架有一个事件发布者的默认实现类{@link SimpleApplicationEventMulticaster},我们可以根据它来自定义自己的事件发布器
     * 这里我们自定义一个SimpleApplicationEventMulticaster,并设置线程池,以实现使用线程池异步发布事件
     *
     * @param taskExecutor {@link ThreadPoolTaskExecutor}
     * @return ApplicationEventMulticaster {@link  SimpleApplicationEventMulticaster}
     *
     * @see AbstractApplicationContext#refresh() spring容器刷新的各个阶段
     * @see AbstractApplicationContext#initApplicationEventMulticaster() 初始化一个事件发布者
     * @see AbstractApplicationContext#registerListeners() 在这个阶段, spring容器会向事件发布者注册15个内部的事件监听器
     * @see AbstractApplicationContext#APPLICATION_EVENT_MULTICASTER_BEAN_NAME
     * @see AbstractApplicationContext#finishBeanFactoryInitialization(ConfigurableListableBeanFactory) 在这个方法里,会把其它我们自定义的事件监听器注册。
     * @see org.springframework.context.support.ApplicationListenerDetector 这个Bean后处理器,会检查Bean是否继承了{@link ApplicationListener}接口,如果是的话,则会把这个bean注册到事件发布器
     */
    @Bean(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
    ApplicationEventMulticaster customApplicationEventMulticaster(ThreadPoolTaskExecutor taskExecutor){
        SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
        eventMulticaster.setTaskExecutor(taskExecutor);
        return eventMulticaster;
    }

    @Bean(AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME)
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //核心线程池大小
        executor.setCorePoolSize(10);
        //最大线程数
        executor.setMaxPoolSize(20);
        //队列容量
        executor.setQueueCapacity(200);
        //活跃时间
        executor.setKeepAliveSeconds(60);
        //线程名字前缀
        executor.setThreadNamePrefix("TaskExecutor-");
        // setRejectedExecutionHandler：当pool已经达到max size的时候，如何处理新任务
        // CallerRunsPolicy：不在新线程中执行任务，而是由调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.initialize();
        return executor;
    }
}
